#ifndef __UI_SOCKET_H__
#define __UI_SOCKET_H__ 1

#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "properties_reader.h"

class CUISocket
{
public:
	static CUISocket* getInstance();
	static void destroy();

	bool setPropertiesReader(CPropertiesReader* pConfig);

	/**
	 * create a new server thread for user interface socket.
	 * Name/IP and Port are used from config.
	 */
	bool startUIServer();

	/**
	 * @return True for start signal and false for no start signal.
	 *         No start signal -> main programm should exit
	 */
	bool waitForUIClientAndStartSignal();
	void setAmpMultiplier(double amp);
	double getAmpMultiplier();

	inline void setRunning(bool* pRunning) { mRunning = pRunning; }


	/*------------------ start of send command methodes ------------------*/

	/* All send methodes must be sync with mutex mCommandSendSync. */

	/**
	 * @note sync with mCommandSendSync
	 */
	bool sendInfo(uint8_t pLogLevel, 
			const char* pDate, int pDateLen,
			const char* pInfo, int pInfoLen);

	/**
	 * @note sync by sendCommand (which use mCommandSendSync)
	 */
	void sendExtractBufferInfo(uint32_t pUsed, uint32_t pSize);

	/**
	 * @note sync by sendCommand (which use mCommandSendSync)
	 */
	void sendFrameSendLength(uint32_t pLength);

	/**
	 * @note sync by sendCommand (which use mCommandSendSync)
	 */
	void sendFrameRecvLength(uint32_t pLength, bool pError);

	/**
	 * @note sync by sendCommand (which use mCommandSendSync)
	 */
	void sendPacketSendLength(uint32_t pLength);

	/**
	 * @note sync by sendCommand (which use mCommandSendSync)
	 */
	void sendPacketRecvLength(uint32_t pLength, bool pCrcError);

	/**
	 * @note sync by sendCommand (which use mCommandSendSync)
	 */
	void sendStreamSendInfo(int pId, int pAck, uint32_t pLength, bool pReplay);

	/**
	 * @note sync by sendCommand (which use mCommandSendSync)
	 */
	void sendStreamSendData(void* pData, int pLength);

	/**
	 * @note sync by sendCommand (which use mCommandSendSync)
	 */
	void sendStreamRecvInfo(int pId, int pAck, uint32_t pLength, bool pReplay);

	/**
	 * @note sync by sendCommand (which use mCommandSendSync)
	 */
	void sendStreamRecvData(void* pData, int pLength);

	/**
	 * @note sync by sendCommand (which use mCommandSendSync)
	 */
	bool sendProtocolOpen();

	/**
	 * @note sync by sendCommand (which use mCommandSendSync)
	 */
	void sendProtocolWrite(void* pData, int pLength);

	/**
	 * @note sync by sendCommand (which use mCommandSendSync)
	 */
	void sendProtocolClose();

	/**
	 * Send feedback to GUI how much bytes already readed with getNextProtocolByte
	 * from mProtocolData buffer.
	 *
	 * @note sync by sendCommand (which use mCommandSendSync)
	 */
	void sendProtocolReadFeedback(uint32_t pRead, uint32_t pSize);

	void sendAmpEmbed(unsigned char pAmp);
	void sendAmpExtract(unsigned char pAmp);

	/*------------------- end of send command methodes -------------------*/


	/**
	 * @return False for no data, True for data
	 */
	bool getNextProtocolReadByte(unsigned char &pReadValue);

private:
	static pthread_mutex_t sSync;
	static pthread_cond_t sConnectSignal;
	static pthread_cond_t sStartSignal;
	static CUISocket* sInstance;
	static const std::string sIniSection;
	static const uint32_t sMaxDataLength;

	/**
	 * A UI command MUST use this mutex to guarantee the sending as one
	 * data block. Otherwise a command can be currupted (interlaced with a
	 * other command).
	 *
	 * The mutex must be locked before sending the first byte (this also
	 * includes the length-field for the command) and must be unlocked after
	 * sending the last byte.
	 *
	 * @note It's not enougth to sync the methode sendData.
	 */
	pthread_mutex_t mCommandSendSync;

	pthread_mutex_t mSendSync;
	CPropertiesReader* mConfig;

	pthread_mutex_t mAmpSync;
	double mAmpMulti;

	int mServerSocket;
	struct sockaddr_in mServerAddress;
	pthread_t mServerThdId;

	int mClientSocket;
	struct sockaddr_in mClientAddress;
	bool mAllowClients;

	unsigned int mWaitTime; // unit: seconds
	bool mStartSignalValue;

	bool* mRunning;
	bool mShutdown;

	pthread_mutex_t mProtocolSync;
	unsigned int mProtocolDataLength;
	unsigned int mProtocolDataNextIndex;
	unsigned char* mProtocolData;

	CUISocket();
	virtual ~CUISocket();
	CUISocket(const CUISocket& );
	CUISocket& operator=(const CUISocket& );

	static void* pthreadForServerAccept(void* pUISocket);
	void* threadForServerAccept();

	void clientFunction();

	int fullRecv(void *pBuf, int pLength);
	int fullSend(const void *pBuf, int pLength);

	/**
	 * @note Don't sync with mCommandSendSync because this is not a full command.
	 */
	int sendLength(uint32_t pLen);

	/**
	 * @note Don't sync with mCommandSendSync because this is not a full command.
	 */
	int sendData(const void* pPayload, int pLen);

	/**
	 * @brief Create and send a command over the socket to the UI
	 *
	 * @note This methode use the mutex mCommandSendSync for sync!!!
	 * @note This methode create the 4 byte length field. The value of this
	 *       field is pLen + 1 (for command).
	 *
	 * @note A command need not use this methode to create the command header
	 *       and its payload but then it must use the mutex mCommandSendSync
	 *       like the methode sendInfo.
	 *
	 * @param pCommand the command, interpreted by the UI
	 * @param pPayload the payload for the command
	 * @param pLen length of the payload (only, without command-length respectively)
	 * @return true for successful transmission, false otherwise.
	 *         false for pLen < 0 or pPayload-null-pointer and pLen != 0
	 */
	bool sendCommand(uint8_t pCommand, const void* pPayload, int pLen);

	/**
	 * Send one or more COMMAND_TRANSMIT_CONFIG commands and mark the end
	 * with a COMMAND_TRANSMIT_CONFIG_END command.
	 *
	 * @note each command is synced by sendCommand (which use mCommandSendSync)
	 *       But it's not guaranteed that no other command is between the
	 *       commands of this methode.
	 */
	bool sendConfiguration();

	/**
	 * For debug network traffic.
	 */
	std::string printData(unsigned int pOffset, const void *pData, int pLen);
};

#endif
